﻿using System;
using Byleist.Caching;
using Moq;
using NUnit.Framework;

namespace Byleist.UnitTests.Caching
{
    [TestFixture]
    class ValueCacheTests
    {
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Ctor_NullPolicy_Throws()
        {
            new ValueCache<int>(null, null);
        }

        [Test]
        public void GetValue_NothingCached_PoliciesAreCalled()
        {
            var invalidationPolicy = new Mock<ICacheInvalidationPolicy>();
            var retrievingPolicy = new Mock<ICacheValueRetrievingPolicy<int>>();
            var cache = new ValueCache<int>(retrievingPolicy.Object, invalidationPolicy.Object);
            cache.GetValue();
            invalidationPolicy.Verify(obj => obj.CacheRequested(), Times.AtLeastOnce());
            invalidationPolicy.Verify(obj => obj.IsCacheValid(), Times.AtLeastOnce());
            invalidationPolicy.Verify(obj => obj.CacheRenewed(), Times.AtLeastOnce());
            retrievingPolicy.Verify(obj => obj.GetValue());
        }

        [Test]
        public void GetValue_NothingCached_ReturnsValueFromRetrivalPolicy()
        {
            var invalidationPolicy = new Mock<ICacheInvalidationPolicy>();
            var retrievingPolicy = new Mock<ICacheValueRetrievingPolicy<int>>();
            retrievingPolicy.Setup(obj => obj.GetValue()).Returns(() => 42);
            var cache = new ValueCache<int>(retrievingPolicy.Object, invalidationPolicy.Object);
            var value = cache.GetValue();
            Assert.AreEqual(42, value); 
        }

        [Test]
        public void GetValue_ValueCachedAndValid_RetrivalPolicyNotCalled()
        {
            var invalidationPolicy = new Mock<ICacheInvalidationPolicy>();
            var retrievingPolicy = new Mock<ICacheValueRetrievingPolicy<int>>();
            var cache = new ValueCache<int>(retrievingPolicy.Object, invalidationPolicy.Object);
            invalidationPolicy.Setup(obj => obj.IsCacheValid()).Returns(() => true);
            retrievingPolicy.Setup(obj => obj.GetValue()).Throws<Exception>();
            cache.GetValue();
        }

        [Test]
        public void GetValue_ValueCachedAndInvalid_RetrivalPolicyIsCalled()
        {
            var invalidationPolicy = new Mock<ICacheInvalidationPolicy>();
            var retrievingPolicy = new Mock<ICacheValueRetrievingPolicy<int>>();
            retrievingPolicy.Setup(obj => obj.GetValue()).Returns(() => 42);
            var cache = new ValueCache<int>(retrievingPolicy.Object, invalidationPolicy.Object);
            cache.GetValue();
            retrievingPolicy.Setup(obj => obj.GetValue()).Returns(() => 43);
            var value = cache.GetValue();
            Assert.AreEqual(43, value);
        }
    }
}
